package cn.com.sbell.vss.monitor;

import java.io.IOException;

import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpException;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import cn.com.sbell.vss.Util;
import cn.com.sbell.vss.VssConstants;
import cn.com.sbell.vss.util.AlarmRaiser;

public class PerformanceMonitor {
	private static final Log log = LogFactory.getLog(PerformanceMonitor.class);

	private volatile boolean running;

	private volatile boolean reportEnabled;

	private String cmsUrl;

	volatile private long interval = 60000L;

	private String serverType;

	private String localIp;
	private String deviceId;

	private Thread performanceMonitorThread;

	volatile private int times;

	volatile private String[] diskPath;

	volatile private int currTimes = 0;

	private IPerformanceDataGetter performanceDataGetter;

	volatile private int cpuLowThreshold;

	volatile private int memoryLowThreshold;

	volatile private int[] diskLowThreshold;

	volatile private int cpuHighThreshold;

	volatile private int memoryHighThreshold;

	volatile private int[] diskHighThreshold;

	volatile private boolean cpuAlarming = false;

	volatile private boolean[] diskAlarming;

	volatile private boolean memoryAlarming = false;

	private AlarmRaiser alarmRaiser;

	private HttpClient httpClient;

	public void init() {
		performanceDataGetter = PerformanceDataGetterFactory
				.getPerformanceDataGetter();
		log.info("Init PerformanceMonitor ...");
		running = true;
		new Thread() {
			public void run() {
				try {
					Thread.sleep(30000);
				} catch (InterruptedException ex) {
				}
				performanceMonitorThread = new Thread(
						new PerformanceMonitorThread());
				performanceMonitorThread.start();
				log.debug("A new PerformanceMonitorThread created: "
						+ performanceMonitorThread);
			}
		}.start();

	}

	public void destroy() {
		log.info("Request to stop PerformanceMonitor ...");
		running = false;
		if (performanceMonitorThread != null) {
			performanceMonitorThread.interrupt();
		}
		performanceDataGetter = null;
	}

	public class PerformanceMonitorThread implements Runnable {

		public void run() {
			if (performanceDataGetter == null) {
				log
						.warn("PerformanceDataGetter initial failed, PerformanceMonitorThread "
								+ this + " stopped.");
				return;
			}
			diskAlarming = new boolean[diskPath.length];
			log.info("PerformanceMonitorThread " + this + " started.");
			while (running) {
				try {
					long currentTime = System.currentTimeMillis();
					int cpu = performanceDataGetter.getCpuUsage();
					if (log.isInfoEnabled()) {
						log.info("Cpu Usage: " + cpu);
					}
					if (cpu >= cpuHighThreshold && !cpuAlarming) {
						if (currTimes >= times) {
							raisePerformanceAlarm(serverType + "_CPU", String
									.valueOf(cpu), currentTime, false);
							currTimes = 0;
							cpuAlarming = true;
						} else {
							currTimes++;
						}
					} else if (cpu < cpuLowThreshold && cpuAlarming) {
						raisePerformanceAlarm(serverType + "_CPU", String
								.valueOf(cpu), currentTime, true);
						currTimes = 0;
						cpuAlarming = false;
					} else {
						currTimes = 0;
					}
					// //
					int memory = performanceDataGetter.getMemoryUsage();
					if (log.isInfoEnabled()) {
						log.info("Memory Usage: " + memory);
					}
					if (memory >= memoryHighThreshold && !memoryAlarming) {
						raisePerformanceAlarm(serverType + "_MEM", String
								.valueOf(memory), currentTime, false);
						memoryAlarming = true;
					} else if (memory < memoryLowThreshold && memoryAlarming) {
						raisePerformanceAlarm(serverType + "_MEM", String
								.valueOf(memory), currentTime, true);
						memoryAlarming = false;
					}
					// ///
					if (reportEnabled) {
						reportPerformanceData(new String[] { "cpuUsage",
								"memoryUsage" }, new int[] { cpu, memory },
								currentTime);
					}
					// //
					if (diskPath != null && diskPath.length > 0) {
						for (int i = 0; i < diskPath.length; i++) {
							int disk = performanceDataGetter
									.getDiskUsage(diskPath[i]);
							if (log.isInfoEnabled()) {
								log.info("Disk Usage: " + diskPath[i] + "="
										+ memory);
							}
							if (disk >= diskHighThreshold[i]
									&& !diskAlarming[i]) {
								raisePerformanceAlarm(serverType + "_DISKFULL",
										diskPath[i] + ":" + disk, currentTime,
										false);
								diskAlarming[i] = true;
							} else if (disk < diskLowThreshold[i]
									&& diskAlarming[i]) {
								raisePerformanceAlarm(serverType + "_DISKFULL",
										diskPath[i] + ":" + disk, currentTime,
										true);
								diskAlarming[i] = false;
							}
							if (false && reportEnabled && disk > 0) {
								// TODO:remove false
								reportPerformanceData(
										new String[] { "diskUsage" },
										new int[] { disk }, currentTime);
							}
						}
					}

				} catch (Throwable ex) {
					log.warn("Exception", ex);
				}
				try {
					Thread.sleep(interval);
				} catch (InterruptedException ex) {
					log.warn("InterruptedException: " + ex.getMessage());
				}
			}
			log.warn("PerformanceMonitorThread " + this + " stopped.");
		}
	};

	private void raisePerformanceAlarm(String alarmType, String data,
			long timestamp, boolean eliminated) {
		alarmRaiser.send(serverType, deviceId, localIp, alarmType, data, timestamp,
				eliminated);
	}

	private void reportPerformanceData(String[] name, int[] value,
			long currentTime) {
		PostMethod method = new PostMethod(cmsUrl + "/ReportPerformanceData");
		method.getParams().setContentCharset(VssConstants.DEFAULT_ENCODINIG);

		// set param
		for (int i = 0; i < name.length; i++) {
			method.addParameter(name[i], String.valueOf(value[i]));
		}
		method.addParameter("serverType", serverType);
		if (!Util.strIsNull(localIp)) {
			method.addParameter("serverIp", localIp);
		}
		if (!Util.strIsNull(deviceId)) {
			method.addParameter("deviceId", deviceId);
		}
		method.addParameter("timestamp", String.valueOf(currentTime/1000L));

		log.debug("Sending an performance data to EMS ");
		try {
			// Execute the method.
			int statusCode = httpClient.executeMethod(method);
			method.getResponseBody();
			if (statusCode != HttpStatus.SC_OK) {
				log.warn("Method failed: " + method.getStatusLine());
			}
		} catch (HttpException ex) {
			log.warn("HTTP protocol violation: ", ex);
		} catch (IOException ex) {
			log.warn("Http transport error: ", ex);
		} catch (Exception ex) {
			log.warn("MS server error", ex);
		} finally {
			// Release the connection.
			method.releaseConnection();
		}
	}

	public void setDiskPath(String[] diskPath) {
		this.diskPath = diskPath;
	}

	public void setInterval(long interval) {
		this.interval = interval;
	}

	public void setPerformanceDataGetter(
			IPerformanceDataGetter performanceDataGetter) {
		this.performanceDataGetter = performanceDataGetter;
	}

	public void setReportEnabled(boolean reportEnabled) {
		this.reportEnabled = reportEnabled;
	}

	public void setLocalIp(String serverIp) {
		this.localIp = serverIp;
	}

	public void setServerType(String serverType) {
		this.serverType = serverType;
	}

	public void setTimes(int times) {
		this.times = times;
	}

	public void setCmsUrl(String cmsUrl) {
		this.cmsUrl = cmsUrl;
	}

	public Thread getPerformanceMonitorThread() {
		return this.performanceMonitorThread;
	}

	public void setCpuHighThreshold(int cpuHighThreshold) {
		this.cpuHighThreshold = cpuHighThreshold;
	}

	public void setCpuLowThreshold(int cpuLowThreshold) {
		this.cpuLowThreshold = cpuLowThreshold;
	}

	public void setDiskHighThreshold(int[] diskHighThreshold) {
		this.diskHighThreshold = diskHighThreshold;
	}

	public void setDiskLowThreshold(int[] diskLowThreshold) {
		this.diskLowThreshold = diskLowThreshold;
	}

	public void setMemoryHighThreshold(int memoryHighThreshold) {
		this.memoryHighThreshold = memoryHighThreshold;
	}

	public void setMemoryLowThreshold(int memoryLowThreshold) {
		this.memoryLowThreshold = memoryLowThreshold;
	}

	public void setAlarmRaiser(AlarmRaiser alarmRaiser) {
		this.alarmRaiser = alarmRaiser;
	}

	public void setHttpClient(HttpClient httpClient) {
		this.httpClient = httpClient;
	}
	/**
	 * @return the deviceId
	 */
	public String getDeviceId() {
		return deviceId;
	}

	/**
	 * @param deviceId the deviceId to set
	 */
	public void setDeviceId(String deviceId) {
		this.deviceId = deviceId;
	}
}
